// USART functions.

/*
 Required includes:	avr/io.h
					stdint.h
 */

/*
 Copyright 2010 Anthony Oko, Koby Hitchen, Mara Cairo, Shaochen Xu, Tyler Lucas
 
 This file is part of the trajctrl program.
 
 trajctrl is licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 
 http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

/*
 http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=45341
 
 System clock frequency and baud rate:
 "System clocks for perfect USART communications should be multiples of
 1.8432MHz", but "error percentages of less than +/- 2% are acceptable."
 baud_value = system_clock_freq / (baud_rate*16 - 1)
 
 (See Table 71 on pg. 168 of ATMEGA32 datasheet.)
 F_CPU = 16 MHz
   U2X = 0 (asynchronous single speed)
 ------------------------
 baud_rate	UBRR	 error
 ---------  ----    ------
 2400		416		-0.1 %
 4800		207		 0.2 %
 9600		103		 0.2 %
 ...
 38.4k		 25		 0.2 %
 ...
 250k		  3		 0.0 %
 500k		  1		 0.0 %
 1Mbps		  0		 0.0 %
 
38.4kbps seems to be the fastest available, reliable COM1 frequency.
 */

#include "usart.h"

// Initialize the USART
void init_usart(uint32_t baudrate)
{
	uint16_t baud_prescale = ((F_CPU + baudrate * 8L) / (baudrate * 16L) - 1);
// http://www.cs.mun.ca/~paul/cs4723/material/atmel/avr-libc-user-manual-1.6.5/FAQ.html#faq_wrong_baud_rate
	UBRRL |= baud_prescale;				// lower
	UBRRH |= (baud_prescale>>8);		// upper
	
	UCSRB |= (1<<RXEN) | (1<<TXEN);					// turn on Tx and Rx

// register select, asynchronous, even parity, 1 stop bit, 8 data bits, clock pol. off
	UCSRC |= (1<<URSEL)|(1<<UPM1)|(1<<UCSZ0)|(1<<UCSZ1);	
	return;
}

void tx(char c)
{
	while ((UCSRA & (1<<UDRE)) == 0) {};
	UDR = c;	
	return;
}

char rx()
{
	while ((UCSRA & (1<<RXC)) == 0) {};
	return UDR;
}


// send a string of characters via the USART
void txstr(const char *s)
{
	uint8_t i=0;
	do {
		tx(s[i]);
	} while (s[i++] != '\0');
	return;
}

// gets characters until stop char or max length
/*void rxstr_EOF(const char *stop,uint8_t max, char *s)
{
	uint8_t n = strlen(stop);
	char buf[n]; buf[n] = '\0';
	uint8_t i=0;
	if (!max)	// no max length if max=0
	{
		do {
			s[i] = rx();
			echo(s[i]);
			shiftl(buf,buf,n);
			buf[n-1] = s[i++];
		} while (strcmp(buf,stop));
	}
	if (max)
	{
		do {
			s[i] = rx();
			echo(s[i]);
			txstr("\n\r\tbuffer (1): "); txstr(buf);
			shiftl(buf,buf,n);
			txstr("\n\r\tbuffer (2): "); txstr(buf);
			buf[n-1] = s[i++];
			txstr("\n\r\tbuffer (3): "); txstr(buf);
			txstr("\tstop: "); txstr(stop); txstr("\n\n\r");
		} while ((i<max) && strcmp(buf,stop));
	}
	return;
}*/

// gets characters until escape key
char *rxstr_ESC(uint8_t max, char *s)
{
   rc i; i.col = 0; i.row = 0;
   uint16_t n = 0;

   while ((s[n] != '\e') && (n<max))
   {   
      s[n++] = rx();
      echo(s[n-1]);

      if (i.col == 60)      // goto next line if at last column
      {
         txstr("\n\r  ");
         i.col = 2;
         i.row++;
         strcat(s,"\n\r  ");
      }
      
      switch (s[n-1])
      {
         case '\0':
         case 0x7F:   // DEL
         case '\a':
            if (i.col)
               i.col--;
            break;
         case '\b':
            if (i.col>=2)
               i.col -= 2;
            else
               i.col = 0;
            break;
         case '\t':
            if (i.col<55)
            {
               i.col += 2;
               txstr("  ");
            }
            else
            {
               while (++i.col<59)
                  tx(' ');
            }
            break;
         case '\n':
         case '\r':
            i.col = 2;
            txstr("  ");
            break;
         default:
            break;
      }
      i.col++;
   }
   ledmsg("---");
   return s;
} 

// shifts characters in a string to the left
/*void shiftl(char *op, const char *ip, uint8_t len)
{
	uint8_t i=0;
	do {
		op[i] = *ip+i;
	} while (i++ < len);
	return;
}*/

void echo(char c)
{
	switch (c)
	{
		case '\0':
			break;
		case '\r':
			txstr("\n\r");
			break;
		case '\b':
			txstr("\b \b");
			break;
		case '\e':
			txstr("\a");
			break;
		case '\t':
			break;				// caller controls tab
		default:
			tx(c);
	}
}

/*void strecho(const char *s)
{
	char c;
	char *stemp = NULL;
	do {
		c = *s++;
		echo(*literal(c,stemp));
	} while(*s != '\0');
}*/

char *literal(char c, char *s)
{
	switch (c)
	{
		case '\0':
			strcpy(s,"\\0");
			break;
		case '\a':
			strcpy(s,"\\a");
			break;
		case '\b':
			strcpy(s,"\\b");
			break;
		case '\t':
			strcpy(s,"\\t");
			break;
		case '\n':
			strcpy(s,"\\n");
			break;
		case '\v':
			strcpy(s,"\\v");
			break;
		case '\f':
			strcpy(s,"\\f");
			break;
		case '\r':
			strcpy(s,"\\r");
			break;
		case '\e':
			strcpy(s,"\\e");
			break;
		default:
			strcpy(s," \0");
			s[0] = c;
	}
	return s;
}
